package com.twitter.home_mixer.product.scored_tweets.scorer
//当然可以。这段代码定义了一个名为 DiversityDiscountProvider 的特性（trait），它提供了一个框架来计算推文候选项的多样性折扣。
// 此外，AuthorDiversityDiscountProvider 对象实现了这个特性，提供了一个特定的多样性折扣逻辑，专注于作者的多样性。
// 让我们逐行分析这个特性和对象的代码逻辑。
import com.twitter.home_mixer.model.HomeFeatures.AuthorIdFeature
import com.twitter.home_mixer.model.HomeFeatures.ScoreFeature
import com.twitter.product_mixer.component_library.model.candidate.TweetCandidate
import com.twitter.product_mixer.core.model.common.CandidateWithFeatures

trait DiversityDiscountProvider {

  /**
   * Fetch the ID of the entity to diversify
   */
  //这个函数用于获取每个候选推文的实体 ID。在这个上下文中，实体可能指的是推文的作者。
  def entityId(candidate: CandidateWithFeatures[TweetCandidate]): Option[Long]

  /**
   * Compute discount factor for each candidate based on position (zero-based)
   * relative to other candidates associated with the same entity
   */
  //这个函数根据候选项在其实体组中的位置（如同一作者的推文）计算折扣因子。
  def discount(position: Int): Double

  /**
   * Return candidate IDs sorted by score in descending order
   */
  //这个函数将候选推文按分数降序排序并返回排序后的推文 ID 列表。
  def sort(candidates: Seq[CandidateWithFeatures[TweetCandidate]]): Seq[Long] = candidates
    .map { candidate =>
      (candidate.candidate.id, candidate.features.getOrElse(ScoreFeature, None).getOrElse(0.0))
    }
    .sortBy(_._2)(Ordering.Double.reverse)
    .map(_._1)

  /**
   * Group by the specified entity ID (e.g. authors, likers, followers)
   * Sort each group by score in descending order
   * Determine the discount factor based on the position of each candidate
   */
    //这个函数是特性的主要逻辑部分。它首先根据实体 ID（例如作者 ID）对候选推文进行分组，
  // 然后对每组内的推文按分数排序，最后根据每个推文在其组中的位置应用折扣因子。
  def apply(
    candidates: Seq[CandidateWithFeatures[TweetCandidate]]
  ): Map[Long, Double] = candidates
    .groupBy(entityId)
    .flatMap {
      case (entityIdOpt, entityCandidates) =>
        val sortedCandidateIds = sort(entityCandidates)

        if (entityIdOpt.isDefined) {
          sortedCandidateIds.zipWithIndex.map {
            case (candidateId, index) =>
              candidateId -> discount(index)
          }
        } else sortedCandidateIds.map(_ -> 1.0)
    }
}
//根据推文在其作者组中的位置，使用指数衰减公式计算折扣因子。这样，同一作者的第一条推文会获得最高的折扣，随着位置的增加，折扣会逐渐减少。
object AuthorDiversityDiscountProvider extends DiversityDiscountProvider {
  private val Decay = 0.5
  private val Floor = 0.25

  override def entityId(candidate: CandidateWithFeatures[TweetCandidate]): Option[Long] =
    candidate.features.getOrElse(AuthorIdFeature, None)

  // Provides an exponential decay based discount by position (with a floor)
  override def discount(position: Int): Double =
    (1 - Floor) * Math.pow(Decay, position) + Floor
}
